package main

import (
	"./luahelper"
	"./tests"
	"fmt"
	"github.com/gocolly/colly"
	lua "github.com/yuin/gopher-lua"
	"golang.org/x/net/html"
	"log"
	"net/http"
	"reflect"
	"strings"
)

type hanlder struct {
	Name string
	Func lua.LValue
}

func main() {
	l := lua.NewState()
	defer l.Close()

	person := &tests.Person{
		Name: "Xiaoming",
		Age:  18,
	}

	println("Method Count", reflect.ValueOf(&person).Elem().NumMethod())

	son := luahelper.ConvertGoInstance2LuaTable(l, person)
	l.SetGlobal("Son", son)
	err := l.DoFile("main.lua")
	if err != nil {
		log.Panic(err)
	}

	return

	c := colly.NewCollector()
	l.SetGlobal("Visit", l.NewFunction(func(state *lua.LState) int {
		url := state.ToString(1)
		if url == "" {
			//fmt.Println("can not Visit a empty url")
		} else {
			c.Visit(url)
		}

		return 0
	}))

	err = l.DoFile("main.lua")

	if err != nil {
		log.Panic(err)
	}

	if table, ok := l.GetGlobal("CollySetting").(*lua.LTable); ok {

		// 允许的域名
		if domains, ok := table.RawGetString("AllowedDomains").(*lua.LTable); ok {
			var allowdDomains []string

			for i := 1; i <= domains.Len(); i++ {
				allowdDomains = append(allowdDomains, lua.LVAsString(domains.RawGetInt(i)))
			}

			c.AllowedDomains = allowdDomains
		}

		if htmlHandle, ok := table.RawGetString("HtmlHandle").(*lua.LTable); ok {
			htmlHandle.ForEach(func(key lua.LValue, value lua.LValue) {
				if key.Type() == lua.LTString && value.Type() == lua.LTFunction {
					c.OnHTML(lua.LVAsString(key), func(element *colly.HTMLElement) {
						defer func() {
							if err, ok := recover().(error); ok {
								log.Println(err.Error())
							}
						}()

						l.CallByParam(lua.P{
							Fn:      value,
							NRet:    0,
							Protect: false,
							Handler: nil,
						}, ConvertElement2LuaTable(l, element))
					})
				} else {
					fmt.Println("unknow data type, key shoud be stirng, value shoud be function")
				}
			})
		}

		c.OnRequest(func(request *colly.Request) {
			defer func() {
				if err, ok := recover().(error); ok {
					log.Println(err.Error())
				}
			}()

			fmt.Println("Request", request.URL)
		})

		onResponse := table.RawGetString("OnResponse")
		if onResponse != nil {
			if onResponse.Type() == lua.LTFunction {
				c.OnResponse(func(response *colly.Response) {
					defer func() {
						if err, ok := recover().(error); ok {
							log.Println(err.Error())
						}
					}()

					l.CallByParam(lua.P{Fn: onResponse,}, lua.LString(response.Request.URL.String()), ConvertCollyResponse2LuaTable(l, response))
				})
			} else if onResponseTable, ok := onResponse.(*lua.LTable); ok {
				//funcMappings := make(map[string]lua.LValue)
				var funcMappings []hanlder
				onResponseTable.ForEach(func(key lua.LValue, value lua.LValue) {
					if key.Type() == lua.LTString && value.Type() == lua.LTFunction {
						//funcMappings[lua.LVAsString(key)] = value
						funcMappings = append(funcMappings, hanlder{
							Name: lua.LVAsString(key),
							Func: value,
						})
					} else {
						fmt.Println("unknow data type, key shoud be stirng, value shoud be function")
					}
				})

				c.OnResponse(func(response *colly.Response) {
					defer func() {
						if err, ok := recover().(error); ok {
							log.Println(err.Error())
						}
					}()

					for _, v := range funcMappings {
						if strings.HasPrefix(response.Request.URL.Path, v.Name) {
							l.CallByParam(lua.P{Fn: v.Func,}, lua.LString(response.Request.URL.String()), ConvertCollyResponse2LuaTable(l, response))
							return
						}
					}

					fmt.Println("not found handle for", response.Request.URL.String())
				})
			}
		} else {
			c.OnResponse(func(response *colly.Response) {
				defer func() {
					if err, ok := recover().(error); ok {
						log.Println(err.Error())
					}
				}()

				fmt.Println("Response Back", response.Request.URL.String())
			})
		}

		visit := lua.LVAsString(table.RawGetString("Visit"))

		if visit == "" {
			log.Println("not found start url")
			return
		}

		err := c.Visit(visit)
		if err != nil {
			log.Panic(err)
		}
	} else {
		print("not found CollySetting")
	}
}

func ConvertElement2LuaTable(l *lua.LState, element *colly.HTMLElement) (table *lua.LTable) {
	table = l.NewTable()

	table.RawSetString("Name", lua.LString(element.Name))
	table.RawSetString("Request", ConvertCollyRequest2LuaTable(l, element.Request))
	table.RawSetString("Response", ConvertCollyResponse2LuaTable(l, element.Response))
	table.RawSetString("Text", lua.LString(element.Text))
	//table.RawSetString("DOM", lua.LString(element.Name))
	table.RawSetString("Index", lua.LNumber(element.Index))

	table.RawSetString("Attr", l.NewFunction(func(state *lua.LState) int {
		attrName := state.ToString(1)
		state.Push(lua.LString(element.Attr(attrName)))
		return 1
	}))

	return
}

func ConvertCollyResponse2LuaTable(l *lua.LState, response *colly.Response) (table *lua.LTable) {
	table = l.NewTable()

	table.RawSetString("StatusCode", lua.LNumber(response.StatusCode))
	table.RawSetString("Body", lua.LString(response.Body))
	table.RawSetString("Headers", ConvertHeaders2LuaTable(l, response.Headers))
	//table.RawSetString("Request", ConvertCollyRequest2LuaTable(l, response.Request))

	return
}

func ConvertHeaders2LuaTable(l *lua.LState, header *http.Header) (table *lua.LTable) {
	table = l.NewTable()

	for k, v := range *header {
		table.RawSetString(k, ConvertStrings2LuaTable(l, v))
	}

	return
}

func ConvertStrings2LuaTable(l *lua.LState, strs []string) (table *lua.LTable) {
	table = l.NewTable()

	for i, item := range strs {
		table.RawSetInt(i+1, lua.LString(item))
	}

	return
}

func ConvertCollyRequest2LuaTable(l *lua.LState, request *colly.Request) (table *lua.LTable) {
	table = l.NewTable()

	table.RawSetString("URL", lua.LString(request.URL.String()))
	table.RawSetString("Depth", lua.LNumber(request.Depth))
	table.RawSetString("Method", lua.LString(request.Method))
	table.RawSetString("ID", lua.LNumber(request.ID))

	table.RawSetString("Visit", l.NewFunction(func(state *lua.LState) int {
		url := state.ToString(1)
		if url == "" {
			//fmt.Println("can not Visit a empty url")
		} else {
			request.Visit(url)
		}

		return 0
	}))

	table.RawSetString("GetAbsUrl", l.NewFunction(func(state *lua.LState) int {
		url := state.ToString(1)
		if url == "" {
			fmt.Println("url is null")
			return 0
		} else {
			state.Push(lua.LString(request.AbsoluteURL(url)))
			return 1
		}
	}))

	return
}

func ConvertHtmlAttributes2LuaTable(l *lua.LState, attributes []html.Attribute) (table *lua.LTable) {
	table = l.NewTable()

	for i, item := range attributes {
		tableItem := ConvertHtmlAttribute2LuaTable(l, item)
		table.RawSetInt(i+1, tableItem)
	}

	return
}

func ConvertHtmlAttribute2LuaTable(l *lua.LState, attribute html.Attribute) (table *lua.LTable) {
	table = l.NewTable()

	table.RawSetString("Namespace", lua.LString(attribute.Namespace))
	table.RawSetString("Key", lua.LString(attribute.Key))
	table.RawSetString("Value", lua.LString(attribute.Val))

	return
}
